package com.example.blog.modules.monitor.service.impl;

import com.example.blog.modules.monitor.dto.SystemMetricsDTO;
import com.example.blog.modules.monitor.dto.MetricsHistoryDTO;
import com.example.blog.modules.monitor.dto.MetricsStatisticsDTO;
import com.example.blog.modules.monitor.entity.MetricsHistory;
import com.example.blog.modules.monitor.repository.MetricsHistoryRepository;
import com.example.blog.modules.monitor.service.SystemMonitorService;
import com.sun.management.OperatingSystemMXBean;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.*;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;
import javax.sql.DataSource;

@Service
@RequiredArgsConstructor
public class SystemMonitorServiceImpl implements SystemMonitorService {
    
    private final StringRedisTemplate redisTemplate;
    private final JdbcTemplate jdbcTemplate;
    private final MetricsHistoryRepository metricsHistoryRepository;
    private final DataSource dataSource;
    
    @Override
    public SystemMetricsDTO getSystemMetrics() {
        SystemMetricsDTO metrics = new SystemMetricsDTO();
        
        // CPU使用率
        OperatingSystemMXBean osMxBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
        double systemCpuLoad = osMxBean.getCpuLoad();
        double processCpuLoad = osMxBean.getProcessCpuLoad();
        
        metrics.setSystemCpuUsage(systemCpuLoad >= 0 ? systemCpuLoad * 100 : 0);
        metrics.setProcessCpuUsage(processCpuLoad >= 0 ? processCpuLoad * 100 : 0);
        
        // 内存使用情况
        MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
        metrics.setHeapMemoryUsed(memoryMXBean.getHeapMemoryUsage().getUsed());
        metrics.setHeapMemoryMax(memoryMXBean.getHeapMemoryUsage().getMax());
        metrics.setNonHeapMemoryUsed(memoryMXBean.getNonHeapMemoryUsage().getUsed());
        
        // 线程信息
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        metrics.setThreadCount(threadMXBean.getThreadCount());
        metrics.setPeakThreadCount(threadMXBean.getPeakThreadCount());
        metrics.setDaemonThreadCount(threadMXBean.getDaemonThreadCount());
        
        return metrics;
    }
    
    @Override
    public long getOnlineUserCount() {
        // 这里假设我们使用Redis的Set来存储在线用户
        return redisTemplate.opsForSet().size("online_users") != null ? 
               redisTemplate.opsForSet().size("online_users") : 0L;
    }
    
    @Override
    public Map<String, String> getSystemInfo() {
        Map<String, String> info = new HashMap<>();
        
        Runtime runtime = Runtime.getRuntime();
        OperatingSystemMXBean osBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
        RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();

        info.put("os.name", System.getProperty("os.name"));
        info.put("os.arch", System.getProperty("os.arch"));
        info.put("os.version", System.getProperty("os.version"));
        info.put("java.version", System.getProperty("java.version"));
        info.put("java.home", System.getProperty("java.home"));
        info.put("user.dir", System.getProperty("user.dir"));
        info.put("jvm.start.time", String.valueOf(runtimeBean.getStartTime()));
        info.put("jvm.uptime", String.valueOf(runtimeBean.getUptime()));
        info.put("available.processors", String.valueOf(osBean.getAvailableProcessors()));
        info.put("total.memory", String.valueOf(runtime.totalMemory()));
        info.put("max.memory", String.valueOf(runtime.maxMemory()));

        return info;
    }
    
    @Override
    public Map<String, Object> getRedisInfo() {
        Map<String, Object> info = new HashMap<>();
        
        try {
            // 获取Redis信息
            Properties props = redisTemplate.getConnectionFactory()
                .getConnection()
                .info();
            
            props.forEach((key, value) -> info.put(key.toString(), value));
            
            // 添加一些额外的Redis统计信息
            info.put("online_users", getOnlineUserCount());
            
        } catch (Exception e) {
            info.put("error", "无法获取Redis信息: " + e.getMessage());
        }
        
        return info;
    }
    
    @Override
    public Map<String, Object> getDatabaseInfo() {
        Map<String, Object> info = new HashMap<>();
        
        try {
            try (var connection = dataSource.getConnection()) {
                info.put("database.product", connection.getMetaData().getDatabaseProductName());
                info.put("database.version", connection.getMetaData().getDatabaseProductVersion());
                info.put("driver.name", connection.getMetaData().getDriverName());
                info.put("driver.version", connection.getMetaData().getDriverVersion());
                info.put("url", connection.getMetaData().getURL());
                info.put("username", connection.getMetaData().getUserName());
                info.put("readonly", connection.isReadOnly());
                info.put("autocommit", connection.getAutoCommit());
                info.put("transaction.isolation", connection.getTransactionIsolation());
            }
        } catch (Exception e) {
            info.put("error", "无法获取数据库信息: " + e.getMessage());
        }
        
        return info;
    }
    
    @Override
    @Scheduled(fixedRate = 60000) // 每分钟执行一次
    @Transactional
    public void recordMetrics() {
        SystemMetricsDTO metrics = getSystemMetrics();
        long onlineUsers = getOnlineUserCount();
        
        MetricsHistory history = new MetricsHistory();
        history.setSystemCpuUsage(metrics.getSystemCpuUsage());
        history.setProcessCpuUsage(metrics.getProcessCpuUsage());
        history.setHeapMemoryUsed(metrics.getHeapMemoryUsed());
        history.setNonHeapMemoryUsed(metrics.getNonHeapMemoryUsed());
        history.setTotalMemory(metrics.getHeapMemoryMax());
        history.setFreeMemory(metrics.getHeapMemoryMax() - metrics.getHeapMemoryUsed());
        history.setThreadCount(metrics.getThreadCount());
        history.setPeakThreadCount(metrics.getPeakThreadCount());
        history.setDaemonThreadCount(metrics.getDaemonThreadCount());
        history.setOnlineUserCount(onlineUsers);
        
        metricsHistoryRepository.save(history);
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<MetricsHistoryDTO> getMetricsHistory(LocalDateTime start, LocalDateTime end) {
        return metricsHistoryRepository.findByTimestampBetweenOrderByTimestampAsc(start, end)
                .stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<MetricsHistoryDTO> getRecentMetrics(int n) {
        return metricsHistoryRepository.findTopNByOrderByTimestampDesc(n)
                .stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public MetricsStatisticsDTO getMetricsStatistics(LocalDateTime start, LocalDateTime end) {
        List<MetricsHistory> histories = metricsHistoryRepository.findByTimestampBetweenOrderByTimestampAsc(start, end);
        
        MetricsStatisticsDTO stats = new MetricsStatisticsDTO();
        if (!histories.isEmpty()) {
            stats.setAvgSystemCpuUsage(histories.stream()
                    .mapToDouble(MetricsHistory::getSystemCpuUsage)
                    .average()
                    .orElse(0.0));
            
            stats.setAvgProcessCpuUsage(histories.stream()
                    .mapToDouble(MetricsHistory::getProcessCpuUsage)
                    .average()
                    .orElse(0.0));
            
            stats.setAvgHeapMemoryUsed(histories.stream()
                    .mapToDouble(h -> h.getHeapMemoryUsed())
                    .average()
                    .orElse(0.0));
            
            stats.setAvgThreadCount(histories.stream()
                    .mapToDouble(h -> h.getThreadCount())
                    .average()
                    .orElse(0.0));
            
            stats.setMaxOnlineUsers(histories.stream()
                    .mapToLong(MetricsHistory::getOnlineUserCount)
                    .max()
                    .orElse(0L));
        }
        
        return stats;
    }
    
    @Override
    @Transactional
    public void cleanupHistory(LocalDateTime before) {
        metricsHistoryRepository.deleteByTimestampBefore(before);
    }
    
    private MetricsHistoryDTO convertToDTO(MetricsHistory entity) {
        MetricsHistoryDTO dto = new MetricsHistoryDTO();
        dto.setTimestamp(entity.getTimestamp());
        dto.setSystemCpuUsage(entity.getSystemCpuUsage());
        dto.setProcessCpuUsage(entity.getProcessCpuUsage());
        dto.setHeapMemoryUsed(entity.getHeapMemoryUsed());
        dto.setNonHeapMemoryUsed(entity.getNonHeapMemoryUsed());
        dto.setTotalMemory(entity.getTotalMemory());
        dto.setFreeMemory(entity.getFreeMemory());
        dto.setThreadCount(entity.getThreadCount());
        dto.setPeakThreadCount(entity.getPeakThreadCount());
        dto.setDaemonThreadCount(entity.getDaemonThreadCount());
        dto.setOnlineUserCount(entity.getOnlineUserCount());
        dto.setRequestCount(entity.getRequestCount());
        dto.setAverageResponseTime(entity.getAverageResponseTime());
        return dto;
    }
} 